package com.tosh;

import com.tosh.model.Bean;
import com.tosh.model.ConstructorArg;
import com.tosh.model.Property;

import javax.xml.stream.XMLEventReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.events.Attribute;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.io.BufferedInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import static com.tosh.HelpUtils.buildBean;
import static com.tosh.HelpUtils.buildConstructorArg;
import static com.tosh.HelpUtils.buildProperty;
import static com.tosh.Utils.isNOE;

/**
 * User: arsentyev
 * Date: 31.07.12
 */
public class XmlBeanFactory {
    private final XMLInputFactory _fac = XMLInputFactory.newInstance();
    private final InputStream is;

    public XmlBeanFactory(InputStream is) {
        this.is = is;
    }

    public List<Bean> getBeans() throws XMLStreamException {
        List<Bean> beans = new ArrayList<Bean>();
        XMLEventReader reader = null;
        String clazz = null, id = null, name = null, ref = null, elemName = null, value = null, indexS = null;
        int index = 0;
        List<Property> props = new ArrayList<Property>();
        Iterator<Attribute> attributes = null;
        Attribute attribute = null;

        List<ConstructorArg> constructorArgs = new ArrayList<ConstructorArg>();
        Bean bean = null;
        Property property = null;
        ConstructorArg constructorArg = null;

        reader = _fac.createXMLEventReader(new BufferedInputStream(this.is));

        while (reader.hasNext()) {
            XMLEvent event = reader.nextEvent();

            if (event.getEventType() == XMLEvent.START_ELEMENT) {
                StartElement startElement = event.asStartElement();
                elemName = startElement.getName().getLocalPart();

                switch (XmlName.getByName(elemName)) {
                    case BEAN: {
                        attributes = (Iterator<Attribute>) startElement.getAttributes();

                        while (attributes.hasNext()) {
                            attribute = attributes.next();
                            switch (XmlName.getByName(attribute.getName().toString())) {
                                case ID: {
                                    id = attribute.getValue();
                                    break;
                                }
                                case CLASS: {
                                    clazz = attribute.getValue();
                                }
                            }
                        }
                        break;
                    }

                    case PROP: {
                        attributes = (Iterator<Attribute>) startElement.getAttributes();

                        while (attributes.hasNext()) {
                            attribute = attributes.next();
                            switch (XmlName.getByName(attribute.getName().toString())) {
                                case NAME: {
                                    name = attribute.getValue();
                                    break;
                                }
                                case REF: {
                                    ref = attribute.getValue();
                                }
                            }
                        }
                        break;
                    }

                    case CONSTRUCTOR: {
                        attributes = (Iterator<Attribute>) startElement.getAttributes();

                        while (attributes.hasNext()) {
                            attribute = attributes.next();
                            switch (XmlName.getByName(attribute.getName().toString())) {
                                case INDEX: {
                                    indexS = attribute.getValue();
                                    break;
                                }
                                case VALUE: {
                                    value = attribute.getValue();
                                }
                            }
                        }
                        break;
                    }
                }
            }

            if (event.getEventType() == XMLEvent.END_ELEMENT) {
                EndElement endElement = event.asEndElement();
                elemName = endElement.getName().getLocalPart();

                switch (XmlName.getByName(elemName)) {
                    case BEAN: {
                        bean = buildBean(id, clazz, props, constructorArgs);
                        if(bean != null) {
                            beans.add(bean);
                        }
                        props = new ArrayList<Property>();
                        constructorArgs = new ArrayList<ConstructorArg>();
                        id = null;
                        clazz = null;
                        index = 0;
                        break;
                    }

                    case PROP: {
                        property = buildProperty(name, ref);
                        if(property != null) {
                            props.add(property);
                        }
                        name = null;
                        ref = null;
                        break;
                    }

                    case CONSTRUCTOR: {
                        if(isNOE(indexS)) {
                            constructorArg = buildConstructorArg(index, value);
                        } else {
                            constructorArg = buildConstructorArg(Utils.parseInt(indexS), value);
                        }
                        if(constructorArg != null) {
                            constructorArgs.add(constructorArg);
                        }
                        indexS = null;
                        value = null;
                        index++;
                        break;
                    }
                }
            }
        }

        return beans;
    }
}